// Advanced query system for code graphs
use crate::{
    CodeNode, CodeEdge, NodeType, EdgeType, NodeId, EdgeId, Result,
    GraphQuery, QueryResult,
};
use std::collections::{HashMap, HashSet, VecDeque};
use serde::{Deserialize, Serialize};

/// Advanced query engine for code graphs
pub struct GraphQueryEngine {
    /// Cache for query results
    query_cache: HashMap<String, CachedQueryResult>,
    /// Configuration for query execution
    config: QueryConfig,
}

/// Configuration for query execution
#[derive(Debug, Clone)]
pub struct QueryConfig {
    /// Maximum number of results to return
    pub max_results: usize,
    /// Maximum depth for graph traversal
    pub max_depth: u32,
    /// Whether to enable query result caching
    pub enable_caching: bool,
    /// Timeout for query execution in milliseconds
    pub timeout_ms: u64,
}

impl Default for QueryConfig {
    fn default() -> Self {
        Self {
            max_results: 1000,
            max_depth: 10,
            enable_caching: true,
            timeout_ms: 5000,
        }
    }
}

/// Cached query result with metadata
#[derive(Debug, Clone)]
struct CachedQueryResult {
    result: QueryResult,
    timestamp: std::time::Instant,
    query_hash: String,
}

/// Advanced query builder for constructing complex queries
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct QueryBuilder {
    /// Node type filters
    pub node_types: Vec<NodeType>,
    /// Edge type filters
    pub edge_types: Vec<EdgeType>,
    /// Language filters
    pub languages: Vec<String>,
    /// Name pattern (regex)
    pub name_pattern: Option<String>,
    /// Traversal specifications
    pub traversal: Option<TraversalSpec>,
    /// Aggregation specifications
    pub aggregation: Option<AggregationSpec>,
    /// Sorting specifications
    pub sorting: Option<SortingSpec>,
    /// Limit and offset
    pub limit: Option<usize>,
    pub offset: Option<usize>,
}

/// Traversal specification for graph walking
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct TraversalSpec {
    /// Starting nodes (by ID or pattern)
    pub start_nodes: Vec<NodeSelector>,
    /// Direction of traversal
    pub direction: TraversalDirection,
    /// Maximum depth
    pub max_depth: u32,
    /// Edge types to follow
    pub follow_edges: Vec<EdgeType>,
    /// Stop conditions
    pub stop_conditions: Vec<StopCondition>,
}

/// Node selector for specifying starting points
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum NodeSelector {
    ById(NodeId),
    ByName(String),
    ByType(NodeType),
    ByPattern(String),
}

/// Direction of graph traversal
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum TraversalDirection {
    Outgoing,
    Incoming,
    Both,
}

/// Stop condition for traversal
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum StopCondition {
    MaxDepth(u32),
    NodeType(NodeType),
    NodeName(String),
    EdgeType(EdgeType),
}

/// Aggregation specification
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct AggregationSpec {
    pub group_by: Vec<GroupByField>,
    pub aggregations: Vec<Aggregation>,
}

/// Fields to group by
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum GroupByField {
    NodeType,
    Language,
    EdgeType,
    Custom(String),
}

/// Aggregation operations
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum Aggregation {
    Count,
    CountDistinct(String),
    Sum(String),
    Average(String),
    Min(String),
    Max(String),
}

/// Sorting specification
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SortingSpec {
    pub fields: Vec<SortField>,
}

/// Sort field specification
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SortField {
    pub field: String,
    pub direction: SortDirection,
}

/// Sort direction
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum SortDirection {
    Ascending,
    Descending,
}

/// Extended query result with additional metadata
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ExtendedQueryResult {
    pub nodes: Vec<NodeId>,
    pub edges: Vec<EdgeId>,
    pub paths: Vec<GraphPath>,
    pub aggregations: HashMap<String, serde_json::Value>,
    pub metadata: HashMap<String, serde_json::Value>,
    pub execution_time_ms: u64,
    pub total_count: usize,
    pub has_more: bool,
}

/// A path through the graph
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct GraphPath {
    pub nodes: Vec<NodeId>,
    pub edges: Vec<EdgeId>,
    pub length: usize,
    pub weight: f64,
}

impl GraphQueryEngine {
    pub fn new() -> Self {
        Self {
            query_cache: HashMap::new(),
            config: QueryConfig::default(),
        }
    }

    pub fn with_config(config: QueryConfig) -> Self {
        Self {
            query_cache: HashMap::new(),
            config,
        }
    }

    /// Execute a basic graph query
    pub async fn execute_query(
        &mut self,
        query: &GraphQuery,
        nodes: &[&dyn CodeNode],
        edges: &[&dyn CodeEdge],
    ) -> Result<QueryResult> {
        let start_time = std::time::Instant::now();

        // Check cache first
        let query_hash = self.compute_query_hash(query);
        if self.config.enable_caching {
            if let Some(cached) = self.query_cache.get(&query_hash) {
                // Check if cache is still valid (simple time-based expiration)
                if cached.timestamp.elapsed().as_secs() < 300 { // 5 minutes
                    return Ok(cached.result.clone());
                }
            }
        }

        // Execute the query
        let mut result_nodes = Vec::new();
        let mut result_edges = Vec::new();

        // Apply node filters
        for node in nodes {
            if self.matches_node_filters(*node, query) {
                result_nodes.push(node.id());
            }
        }

        // Apply edge filters
        for edge in edges {
            if self.matches_edge_filters(*edge, query) {
                result_edges.push(edge.id());
            }
        }

        // Apply limit and offset
        if let Some(limit) = query.limit {
            if let Some(offset) = query.offset {
                result_nodes = result_nodes.into_iter().skip(offset).take(limit).collect();
            } else {
                result_nodes = result_nodes.into_iter().take(limit).collect();
            }
        }

        let execution_time = start_time.elapsed().as_millis() as u64;
        let result = QueryResult {
            nodes: result_nodes,
            edges: result_edges,
            metadata: HashMap::new(),
            execution_time_ms: execution_time,
        };

        // Cache the result
        if self.config.enable_caching {
            self.query_cache.insert(query_hash, CachedQueryResult {
                result: result.clone(),
                timestamp: std::time::Instant::now(),
                query_hash: self.compute_query_hash(query),
            });
        }

        Ok(result)
    }

    /// Execute an advanced query with traversal and aggregation
    pub async fn execute_advanced_query(
        &mut self,
        builder: &QueryBuilder,
        nodes: &[&dyn CodeNode],
        edges: &[&dyn CodeEdge],
    ) -> Result<ExtendedQueryResult> {
        let start_time = std::time::Instant::now();

        // Build node and edge lookup maps
        let node_map: HashMap<NodeId, &dyn CodeNode> = nodes
            .iter()
            .map(|node| (node.id(), *node))
            .collect();

        let edge_map: HashMap<EdgeId, &dyn CodeEdge> = edges
            .iter()
            .map(|edge| (edge.id(), *edge))
            .collect();

        // Build adjacency lists for efficient traversal
        let adjacency = self.build_adjacency_lists(edges);

        let mut result_nodes = Vec::new();
        let mut result_edges = Vec::new();
        let mut paths = Vec::new();

        // Execute traversal if specified
        if let Some(traversal) = &builder.traversal {
            let traversal_result = self.execute_traversal(
                traversal,
                &node_map,
                &edge_map,
                &adjacency,
            ).await?;
            
            result_nodes.extend(traversal_result.nodes);
            result_edges.extend(traversal_result.edges);
            paths.extend(traversal_result.paths);
        } else {
            // Simple filtering
            for node in nodes {
                if self.matches_advanced_node_filters(*node, builder) {
                    result_nodes.push(node.id());
                }
            }
        }

        // Execute aggregations if specified
        let aggregations = if let Some(agg_spec) = &builder.aggregation {
            self.execute_aggregations(agg_spec, &result_nodes, &node_map).await?
        } else {
            HashMap::new()
        };

        // Apply sorting if specified
        if let Some(sort_spec) = &builder.sorting {
            self.apply_sorting(&mut result_nodes, sort_spec, &node_map);
        }

        // Apply limit and offset
        let total_count = result_nodes.len();
        let has_more = if let Some(limit) = builder.limit {
            if let Some(offset) = builder.offset {
                result_nodes = result_nodes.into_iter().skip(offset).take(limit).collect();
                total_count > offset + limit
            } else {
                result_nodes = result_nodes.into_iter().take(limit).collect();
                total_count > limit
            }
        } else {
            false
        };

        let execution_time = start_time.elapsed().as_millis() as u64;

        Ok(ExtendedQueryResult {
            nodes: result_nodes,
            edges: result_edges,
            paths,
            aggregations,
            metadata: HashMap::new(),
            execution_time_ms: execution_time,
            total_count,
            has_more,
        })
    }

    /// Find shortest paths between nodes
    pub async fn find_shortest_paths(
        &self,
        start_node: NodeId,
        end_node: NodeId,
        edges: &[&dyn CodeEdge],
        max_depth: Option<u32>,
    ) -> Result<Vec<GraphPath>> {
        let adjacency = self.build_adjacency_lists(edges);
        let max_depth = max_depth.unwrap_or(self.config.max_depth);

        // BFS to find shortest paths
        let mut queue = VecDeque::new();
        let mut visited = HashSet::new();
        let mut paths = Vec::new();

        queue.push_back(GraphPath {
            nodes: vec![start_node],
            edges: vec![],
            length: 0,
            weight: 0.0,
        });

        while let Some(current_path) = queue.pop_front() {
            let current_node = *current_path.nodes.last().unwrap();

            if current_node == end_node {
                paths.push(current_path);
                continue;
            }

            if current_path.length >= max_depth as usize {
                continue;
            }

            if visited.contains(&current_node) {
                continue;
            }
            visited.insert(current_node);

            if let Some(neighbors) = adjacency.get(&current_node) {
                for &(neighbor_node, edge_id) in neighbors {
                    let mut new_path = current_path.clone();
                    new_path.nodes.push(neighbor_node);
                    new_path.edges.push(edge_id);
                    new_path.length += 1;
                    new_path.weight += 1.0; // Simple weight calculation

                    queue.push_back(new_path);
                }
            }
        }

        Ok(paths)
    }

    // Helper methods (simplified implementations)
    fn compute_query_hash(&self, query: &GraphQuery) -> String {
        format!("{:?}", query) // Simple hash based on debug representation
    }

    fn matches_node_filters(&self, node: &dyn CodeNode, query: &GraphQuery) -> bool {
        // Check node type filter
        if let Some(ref types) = query.node_types {
            if !types.contains(node.node_type()) {
                return false;
            }
        }

        // Check language filter
        if let Some(ref languages) = query.languages {
            if !languages.contains(&node.language().to_string()) {
                return false;
            }
        }

        // Check name pattern
        if let Some(ref pattern) = query.name_pattern {
            if !node.name().contains(pattern) {
                return false;
            }
        }

        true
    }

    fn matches_edge_filters(&self, edge: &dyn CodeEdge, query: &GraphQuery) -> bool {
        if let Some(ref types) = query.edge_types {
            types.contains(edge.edge_type())
        } else {
            true
        }
    }

    fn matches_advanced_node_filters(&self, node: &dyn CodeNode, builder: &QueryBuilder) -> bool {
        // Similar to matches_node_filters but for QueryBuilder
        if !builder.node_types.is_empty() && !builder.node_types.contains(node.node_type()) {
            return false;
        }

        if !builder.languages.is_empty() && !builder.languages.contains(&node.language().to_string()) {
            return false;
        }

        if let Some(ref pattern) = builder.name_pattern {
            if !node.name().contains(pattern) {
                return false;
            }
        }

        true
    }

    fn build_adjacency_lists(&self, edges: &[&dyn CodeEdge]) -> HashMap<NodeId, Vec<(NodeId, EdgeId)>> {
        let mut adjacency = HashMap::new();

        for edge in edges {
            adjacency
                .entry(edge.source())
                .or_insert_with(Vec::new)
                .push((edge.target(), edge.id()));
        }

        adjacency
    }

    async fn execute_traversal(
        &self,
        traversal: &TraversalSpec,
        node_map: &HashMap<NodeId, &dyn CodeNode>,
        edge_map: &HashMap<EdgeId, &dyn CodeEdge>,
        adjacency: &HashMap<NodeId, Vec<(NodeId, EdgeId)>>,
    ) -> Result<TraversalResult> {
        let mut result_nodes = Vec::new();
        let mut result_edges = Vec::new();
        let mut paths = Vec::new();

        // Find starting nodes
        let start_nodes = self.resolve_node_selectors(&traversal.start_nodes, node_map);

        for start_node in start_nodes {
            let traversal_result = self.traverse_from_node(
                start_node,
                traversal,
                adjacency,
                node_map,
                edge_map,
            ).await?;

            result_nodes.extend(traversal_result.nodes);
            result_edges.extend(traversal_result.edges);
            paths.extend(traversal_result.paths);
        }

        // Remove duplicates
        result_nodes.sort();
        result_nodes.dedup();
        result_edges.sort();
        result_edges.dedup();

        Ok(TraversalResult {
            nodes: result_nodes,
            edges: result_edges,
            paths,
        })
    }

    /// Resolve node selectors to actual node IDs
    fn resolve_node_selectors(
        &self,
        selectors: &[NodeSelector],
        node_map: &HashMap<NodeId, &dyn CodeNode>,
    ) -> Vec<NodeId> {
        let mut result = Vec::new();

        for selector in selectors {
            match selector {
                NodeSelector::ById(id) => {
                    if node_map.contains_key(id) {
                        result.push(*id);
                    }
                }
                NodeSelector::ByName(name) => {
                    for (id, node) in node_map {
                        if node.name() == name {
                            result.push(*id);
                        }
                    }
                }
                NodeSelector::ByType(node_type) => {
                    for (id, node) in node_map {
                        if node.node_type() == node_type {
                            result.push(*id);
                        }
                    }
                }
                NodeSelector::ByPattern(pattern) => {
                    for (id, node) in node_map {
                        if node.name().contains(pattern) {
                            result.push(*id);
                        }
                    }
                }
            }
        }

        result
    }

    /// Traverse from a single node following the traversal specification
    async fn traverse_from_node(
        &self,
        start_node: NodeId,
        spec: &TraversalSpec,
        adjacency: &HashMap<NodeId, Vec<(NodeId, EdgeId)>>,
        node_map: &HashMap<NodeId, &dyn CodeNode>,
        edge_map: &HashMap<EdgeId, &dyn CodeEdge>,
    ) -> Result<TraversalResult> {
        let mut visited = HashSet::new();
        let mut result_nodes = Vec::new();
        let mut result_edges = Vec::new();
        let mut paths = Vec::new();

        // Use DFS with stack
        let mut stack = VecDeque::new();
        stack.push_back((start_node, 0, vec![start_node], vec![])); // (node, depth, path_nodes, path_edges)

        while let Some((current_node, depth, path_nodes, path_edges)) = stack.pop_back() {
            // Check depth limit
            if depth >= spec.max_depth {
                continue;
            }

            // Check stop conditions
            if self.should_stop_traversal(current_node, &spec.stop_conditions, node_map) {
                continue;
            }

            // Add current node to results if not already visited
            if !visited.contains(&current_node) {
                visited.insert(current_node);
                result_nodes.push(current_node);

                // Create path if we've traversed at least one edge
                if !path_edges.is_empty() {
                    paths.push(GraphPath {
                        nodes: path_nodes.clone(),
                        edges: path_edges.clone(),
                        length: path_edges.len(),
                        weight: path_edges.len() as f64,
                    });
                }
            }

            // Get neighbors based on traversal direction
            let neighbors = self.get_neighbors_for_traversal(current_node, &spec.direction, adjacency, edge_map);

            for (neighbor_node, edge_id, edge) in neighbors {
                // Check if edge type should be followed
                if !spec.follow_edges.is_empty() && !spec.follow_edges.contains(edge.edge_type()) {
                    continue;
                }

                // Add edge to results
                if !result_edges.contains(&edge_id) {
                    result_edges.push(edge_id);
                }

                // Continue traversal
                let mut new_path_nodes = path_nodes.clone();
                let mut new_path_edges = path_edges.clone();
                new_path_nodes.push(neighbor_node);
                new_path_edges.push(edge_id);

                stack.push_back((neighbor_node, depth + 1, new_path_nodes, new_path_edges));
            }
        }

        Ok(TraversalResult {
            nodes: result_nodes,
            edges: result_edges,
            paths,
        })
    }

    /// Check if traversal should stop at this node
    fn should_stop_traversal(
        &self,
        node_id: NodeId,
        stop_conditions: &[StopCondition],
        node_map: &HashMap<NodeId, &dyn CodeNode>,
    ) -> bool {
        if let Some(node) = node_map.get(&node_id) {
            for condition in stop_conditions {
                match condition {
                    StopCondition::NodeType(node_type) => {
                        if node.node_type() == node_type {
                            return true;
                        }
                    }
                    StopCondition::NodeName(name) => {
                        if node.name() == name {
                            return true;
                        }
                    }
                    StopCondition::MaxDepth(_depth) => {
                        // This should be handled at the traversal level
                        // but we can add additional logic here if needed
                    }
                    StopCondition::EdgeType(_edge_type) => {
                        // Edge type conditions are handled during edge traversal
                        // not at the node level
                    }
                }
            }
        }
        false
    }

    /// Get neighbors for traversal based on direction
    fn get_neighbors_for_traversal<'a>(
        &self,
        node_id: NodeId,
        direction: &TraversalDirection,
        adjacency: &HashMap<NodeId, Vec<(NodeId, EdgeId)>>,
        edge_map: &'a HashMap<EdgeId, &dyn CodeEdge>,
    ) -> Vec<(NodeId, EdgeId, &'a dyn CodeEdge)> {
        let mut neighbors = Vec::new();

        match direction {
            TraversalDirection::Outgoing => {
                // Follow outgoing edges (where this node is the source)
                if let Some(adj_list) = adjacency.get(&node_id) {
                    for &(neighbor_id, edge_id) in adj_list {
                        if let Some(edge) = edge_map.get(&edge_id) {
                            if edge.source() == node_id {
                                neighbors.push((neighbor_id, edge_id, *edge));
                            }
                        }
                    }
                }
            }
            TraversalDirection::Incoming => {
                // Follow incoming edges (where this node is the target)
                if let Some(adj_list) = adjacency.get(&node_id) {
                    for &(neighbor_id, edge_id) in adj_list {
                        if let Some(edge) = edge_map.get(&edge_id) {
                            if edge.target() == node_id {
                                neighbors.push((neighbor_id, edge_id, *edge));
                            }
                        }
                    }
                }
            }
            TraversalDirection::Both => {
                // Follow both incoming and outgoing edges
                if let Some(adj_list) = adjacency.get(&node_id) {
                    for &(neighbor_id, edge_id) in adj_list {
                        if let Some(edge) = edge_map.get(&edge_id) {
                            neighbors.push((neighbor_id, edge_id, *edge));
                        }
                    }
                }
            }
        }

        neighbors
    }

    async fn execute_aggregations(
        &self,
        _agg_spec: &AggregationSpec,
        _nodes: &[NodeId],
        _node_map: &HashMap<NodeId, &dyn CodeNode>,
    ) -> Result<HashMap<String, serde_json::Value>> {
        // TODO: Implement actual aggregation logic
        Ok(HashMap::new())
    }

    fn apply_sorting(
        &self,
        _nodes: &mut Vec<NodeId>,
        _sort_spec: &SortingSpec,
        _node_map: &HashMap<NodeId, &dyn CodeNode>,
    ) {
        // TODO: Implement actual sorting logic
    }

    /// Clear the query cache
    pub fn clear_cache(&mut self) {
        self.query_cache.clear();
    }

    /// Get cache statistics
    pub fn get_cache_stats(&self) -> CacheStats {
        CacheStats {
            total_entries: self.query_cache.len(),
            total_size_bytes: self.query_cache.len() * 1024, // Rough estimate
        }
    }
}

impl Default for GraphQueryEngine {
    fn default() -> Self {
        Self::new()
    }
}

/// Result of a traversal operation
struct TraversalResult {
    nodes: Vec<NodeId>,
    edges: Vec<EdgeId>,
    paths: Vec<GraphPath>,
}

/// Cache statistics
#[derive(Debug)]
pub struct CacheStats {
    pub total_entries: usize,
    pub total_size_bytes: usize,
}

impl QueryBuilder {
    pub fn new() -> Self {
        Self {
            node_types: Vec::new(),
            edge_types: Vec::new(),
            languages: Vec::new(),
            name_pattern: None,
            traversal: None,
            aggregation: None,
            sorting: None,
            limit: None,
            offset: None,
        }
    }

    pub fn with_node_types(mut self, types: Vec<NodeType>) -> Self {
        self.node_types = types;
        self
    }

    pub fn with_languages(mut self, languages: Vec<String>) -> Self {
        self.languages = languages;
        self
    }

    pub fn with_name_pattern(mut self, pattern: String) -> Self {
        self.name_pattern = Some(pattern);
        self
    }

    pub fn with_limit(mut self, limit: usize) -> Self {
        self.limit = Some(limit);
        self
    }

    pub fn with_offset(mut self, offset: usize) -> Self {
        self.offset = Some(offset);
        self
    }
}

impl Default for QueryBuilder {
    fn default() -> Self {
        Self::new()
    }
}

impl GraphQueryEngine {
    /// Find shortest path between two nodes using Dijkstra's algorithm
    pub async fn find_shortest_path(
        &self,
        source: NodeId,
        target: NodeId,
        nodes: &[&dyn CodeNode],
        edges: &[&dyn CodeEdge],
        max_depth: Option<u32>,
    ) -> Result<Option<GraphPath>> {
        let _node_map: HashMap<NodeId, &dyn CodeNode> = nodes.iter()
            .map(|n| (n.id(), *n))
            .collect();
        let edge_map: HashMap<EdgeId, &dyn CodeEdge> = edges.iter()
            .map(|e| (e.id(), *e))
            .collect();

        // Build adjacency list
        let mut adjacency: HashMap<NodeId, Vec<(NodeId, EdgeId, f64)>> = HashMap::new();
        for edge in edges {
            let weight = self.calculate_edge_weight(*edge);
            adjacency.entry(edge.source())
                .or_insert_with(Vec::new)
                .push((edge.target(), edge.id(), weight));
        }

        // Dijkstra's algorithm
        let mut distances: HashMap<NodeId, f64> = HashMap::new();
        let mut previous: HashMap<NodeId, (NodeId, EdgeId)> = HashMap::new();
        let mut unvisited = HashSet::new();

        // Initialize distances
        for node in nodes {
            let distance = if node.id() == source { 0.0 } else { f64::INFINITY };
            distances.insert(node.id(), distance);
            unvisited.insert(node.id());
        }

        let max_d = max_depth.unwrap_or(self.config.max_depth) as f64;

        while !unvisited.is_empty() {
            // Find unvisited node with minimum distance
            let current = unvisited.iter()
                .min_by(|a, b| {
                    distances.get(a).unwrap_or(&f64::INFINITY)
                        .partial_cmp(distances.get(b).unwrap_or(&f64::INFINITY))
                        .unwrap_or(std::cmp::Ordering::Equal)
                })
                .copied();

            if let Some(current) = current {
                unvisited.remove(&current);

                if current == target {
                    // Reconstruct path
                    return Ok(Some(self.reconstruct_weighted_path(source, target, &previous, &edge_map)));
                }

                let current_distance = *distances.get(&current).unwrap_or(&f64::INFINITY);
                if current_distance >= max_d || current_distance == f64::INFINITY {
                    continue;
                }

                // Update distances to neighbors
                if let Some(neighbors) = adjacency.get(&current) {
                    for &(neighbor, edge_id, weight) in neighbors {
                        if unvisited.contains(&neighbor) {
                            let new_distance = current_distance + weight;
                            let neighbor_distance = *distances.get(&neighbor).unwrap_or(&f64::INFINITY);

                            if new_distance < neighbor_distance {
                                distances.insert(neighbor, new_distance);
                                previous.insert(neighbor, (current, edge_id));
                            }
                        }
                    }
                }
            } else {
                break;
            }
        }

        Ok(None)
    }

    /// Find all paths between two nodes within a depth limit
    pub async fn find_all_paths(
        &self,
        source: NodeId,
        target: NodeId,
        _nodes: &[&dyn CodeNode],
        edges: &[&dyn CodeEdge],
        max_depth: u32,
        max_paths: Option<usize>,
    ) -> Result<Vec<GraphPath>> {
        let mut paths = Vec::new();
        let adjacency = self.build_adjacency_lists(edges);
        let edge_map: HashMap<EdgeId, &dyn CodeEdge> = edges.iter()
            .map(|e| (e.id(), *e))
            .collect();

        let mut current_path = Vec::new();
        let mut current_edges = Vec::new();
        let mut visited = HashSet::new();

        self.dfs_find_paths(
            source,
            target,
            &adjacency,
            &edge_map,
            &mut current_path,
            &mut current_edges,
            &mut visited,
            &mut paths,
            max_depth,
            max_paths.unwrap_or(100),
        );

        Ok(paths)
    }

    /// Perform depth-first search to find all paths
    fn dfs_find_paths(
        &self,
        current: NodeId,
        target: NodeId,
        adjacency: &HashMap<NodeId, Vec<(NodeId, EdgeId)>>,
        edge_map: &HashMap<EdgeId, &dyn CodeEdge>,
        current_path: &mut Vec<NodeId>,
        current_edges: &mut Vec<EdgeId>,
        visited: &mut HashSet<NodeId>,
        paths: &mut Vec<GraphPath>,
        max_depth: u32,
        max_paths: usize,
    ) {
        if paths.len() >= max_paths {
            return;
        }

        current_path.push(current);
        visited.insert(current);

        if current == target {
            // Found a path
            let weight = current_edges.iter()
                .filter_map(|&edge_id| edge_map.get(&edge_id))
                .map(|edge| self.calculate_edge_weight(*edge))
                .sum();

            paths.push(GraphPath {
                nodes: current_path.clone(),
                edges: current_edges.clone(),
                length: current_path.len() - 1,
                weight,
            });
        } else if current_path.len() <= max_depth as usize {
            // Continue searching
            if let Some(neighbors) = adjacency.get(&current) {
                for &(neighbor, edge_id) in neighbors {
                    if !visited.contains(&neighbor) {
                        current_edges.push(edge_id);
                        self.dfs_find_paths(
                            neighbor,
                            target,
                            adjacency,
                            edge_map,
                            current_path,
                            current_edges,
                            visited,
                            paths,
                            max_depth,
                            max_paths,
                        );
                        current_edges.pop();
                    }
                }
            }
        }

        current_path.pop();
        visited.remove(&current);
    }

    /// Calculate weight for an edge (used in pathfinding)
    fn calculate_edge_weight(&self, edge: &dyn CodeEdge) -> f64 {
        match edge.edge_type() {
            EdgeType::Calls => 1.0,
            EdgeType::References => 1.2,
            EdgeType::Inherits => 0.8,
            EdgeType::Implements => 0.8,
            EdgeType::Imports => 1.5,
            EdgeType::Contains => 0.5,
            EdgeType::DependsOn => 1.3,
            EdgeType::Uses => 1.1,
            EdgeType::ControlFlow => 0.7,
            EdgeType::DataFlow => 0.9,
            EdgeType::TypeRelation => 1.0,
            EdgeType::Defines => 0.6,
            EdgeType::Unknown => 2.0,
        }
    }

    /// Reconstruct path from Dijkstra's algorithm result
    fn reconstruct_weighted_path(
        &self,
        source: NodeId,
        target: NodeId,
        previous: &HashMap<NodeId, (NodeId, EdgeId)>,
        edge_map: &HashMap<EdgeId, &dyn CodeEdge>,
    ) -> GraphPath {
        let mut path_nodes = Vec::new();
        let mut path_edges = Vec::new();
        let mut current = target;

        while current != source {
            path_nodes.push(current);
            if let Some((prev_node, edge_id)) = previous.get(&current) {
                path_edges.push(*edge_id);
                current = *prev_node;
            } else {
                break;
            }
        }
        path_nodes.push(source);

        // Reverse to get path from source to target
        path_nodes.reverse();
        path_edges.reverse();

        let weight = path_edges.iter()
            .filter_map(|&edge_id| edge_map.get(&edge_id))
            .map(|edge| self.calculate_edge_weight(*edge))
            .sum();

        GraphPath {
            nodes: path_nodes.clone(),
            edges: path_edges,
            length: path_nodes.len() - 1,
            weight,
        }
    }
}
